package {
	import com.adobe.utils.AGALMiniAssembler;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.Stage3D;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DCompareMode;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DRenderMode;
	import flash.display3D.Context3DTextureFormat;
	import flash.display3D.Context3DTriangleFace;
	import flash.display3D.Context3DVertexBufferFormat;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.Program3D;
	import flash.display3D.VertexBuffer3D;
	import flash.display3D.textures.Texture;
	import flash.events.Event;
	import flash.geom.Matrix3D;
	import flash.geom.Rectangle;
	import flash.geom.Vector3D;
	import flash.utils.getTimer;
	
	public class Cylinder extends Sprite
	{
		private const _width:Number = 800;
		private const _height:Number = 600;
		
		private var _stage:Stage3D;
		
		private var bitmapAry:Vector.<BitmapData> = new Vector.<BitmapData>(8);
		[Embed( source = "assets/1.jpg" )]
		private static var PIC:Class;
		[Embed( source = "assets/2.png" )]
		private static var PIC2:Class;
		
		
		
		private var _context:Context3D;
		private var _indexBuffer:IndexBuffer3D;
		private var _vertexBuffer:VertexBuffer3D;
		private var _program:Program3D;
		private var texture:Texture;
		
		private var _modelView:Matrix3D;
		private var _modelViewProjection:Matrix3D;
		private var _projection:Matrix3D;
		
		private var _time:Number = 0;
		private var _deltaTime:Number = 0;
		private var _tweenTime:Number = 0;
		private var _tweenPitch:Number = 0;
		private var _tweenYaw:Number = 0;
		private var _pitch:Number = 0;
		private var _yaw:Number = 0;
		

		private var _cubeVertexes:Vector.<Number> = new Vector.<Number>;
		private var uvAry:Array = new Array(0.0,1.0,1.0,1.0,1.0,0.0,0.0,0.0);
		private function setBitmapAry():void
		{
			var bitmap:Bitmap = new PIC();
			var bitmapdata:BitmapData = bitmap.bitmapData;
			bitmapAry[0] = bitmapdata;
			bitmapAry[2] = bitmapdata;
			bitmapAry[4] = bitmapdata;
			bitmapAry[6] = bitmapdata;
			
			bitmap = new PIC2();
			bitmapdata = bitmap.bitmapData;
			bitmapAry[1] = bitmapdata;
			bitmapAry[3] = bitmapdata;
			bitmapAry[5] = bitmapdata;
			bitmapAry[7] = bitmapdata;
		}
		
		private var _cubeIndexes:Vector.<uint> = new Vector.<uint>;
		
		private var _vertexShader:String = "m44 op, va0, vc0\n"+
												"mov v0, va1\n";
			
		
		private var _fragmentShader:String = "mov ft0, v0\n" +
											"tex ft1, ft0, fs1 <2d,clamp,linear>\n" +
											"mov oc, ft1\n";
		
		public function Cylinder()
		{
			setBitmapAry();
			setVertexesIndexes();
			addEventListener(Event.ADDED_TO_STAGE, setUp);
		}
		
		private function setUp(event:Event):void 
		{
			_stage = stage.stage3Ds[0];
			_stage.viewPort = new Rectangle(0, 0, _width, _height);
			_stage.addEventListener(Event.CONTEXT3D_CREATE, onContext);
			_stage.requestContext3D(Context3DRenderMode.AUTO);
		}
		
		
		private function onContext(event:Event):void
		{
			_context = _stage.context3D;
			
			if(_context==null)
				return ;
			
			trace(_context.driverInfo)
			_context.configureBackBuffer(_width, _height, 4, true);
			_context.enableErrorChecking = true;
			
            //设置剔除
			_context.setCulling(Context3DTriangleFace.NONE);
			_context.setDepthTest(true, Context3DCompareMode.LESS_EQUAL);			            
			
			//AGAL代码执行
			var vsAssembler:AGALMiniAssembler = new AGALMiniAssembler;
			vsAssembler.assemble(Context3DProgramType.VERTEX, _vertexShader);
			
			var fsAssembler:AGALMiniAssembler = new AGALMiniAssembler;
			fsAssembler.assemble(Context3DProgramType.FRAGMENT, _fragmentShader);
			
			//创建agal程序
			_program = _context.createProgram();
			_program.upload(vsAssembler.agalcode, fsAssembler.agalcode);
			_context.setProgram(_program);
			
            //创建缓冲
			_vertexBuffer = _context.createVertexBuffer(_cubeVertexes.length / 5, 5);
			_vertexBuffer.uploadFromVector(_cubeVertexes, 0, _cubeVertexes.length / 5);
			
			_indexBuffer = _context.createIndexBuffer(_cubeIndexes.length);
			_indexBuffer.uploadFromVector(_cubeIndexes, 0, _cubeIndexes.length);
			
			texture = _context.createTexture(256, 256, Context3DTextureFormat.BGRA,true);
			
			_context.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
			_context.setVertexBufferAt(1, _vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
			_context.setTextureAt(1,this.texture);
			
			
			//世界矩阵
			_modelView = new Matrix3D;
			_modelViewProjection = new Matrix3D;
			_projection = perspectiveProjection(60, _width / _height, 0.1, 2048);
			
			_time = getTimer() / 1000.0;
			_tweenTime = _time + 1;
			
			
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		/**
		 * 每帧运行 
		 * @param event
		 * 
		 */		
		private function onEnterFrame(event:Event):void 
		{
			_context.clear(0, 0, 0);
			
			var newTime:Number = getTimer() / 1000.0;
			_deltaTime = Math.min(newTime - _time, 0.1);
			_time = newTime;
			
			
			_tweenPitch ++;
			_modelView.identity();
			_modelView.appendRotation(_tweenPitch, Vector3D.Y_AXIS);
			_modelView.appendRotation(mouseY, Vector3D.X_AXIS);
			_modelView.appendTranslation(0, 0, -8);

			_modelViewProjection.identity();
			_modelViewProjection.append(_modelView);
			_modelViewProjection.append(_projection);
			_context.setProgramConstantsFromMatrix(
				Context3DProgramType.VERTEX, 0, _modelViewProjection, true);
            
			for(var i:int = 0;i<8;i++)
			{
				texture.uploadFromBitmapData(bitmapAry[i]);
				_context.drawTriangles(_indexBuffer,i*6,2);
			}
			
			_context.present();
		}
		/**
		 * 世界矩阵 
		 * @param fov
		 * @param aspect
		 * @param near
		 * @param far
		 * @return 
		 * 
		 */		
		private function perspectiveProjection(fov:Number=90,aspect:Number=1, 
											   near:Number=1, far:Number=2048):Matrix3D 
		{
			var f:Number =  Math.tan(fov * Math.PI / 360);
			f = 1/f;
			var a:Number = f/aspect;
			var b:Number = f;
			var q:Number = (far+near)/(near-far);
			var qn:Number = (2*far*near)/(near-far);
			return new Matrix3D(Vector.<Number>
				([
					a, 0, 0, 0,
					0, b, 0, 0,
					0, 0, q, -1,
					0, 0, qn, 0
				]));
		}
		private function setVertexesIndexes():void
		{
			var angle:Number = 0;
			var radius:Number = 3;
			var x:Number;
			var z:Number;
			var y:Number;
			var flag:Array = new Array();
			var point:int;
			for(angle=0;angle <= 360;angle+=15){
				x = radius*Math.cos(angle*Math.PI/180);
				z = radius*Math.sin(angle*Math.PI/180);
				y = -1;
				_cubeVertexes.push(x,y,z);
				_cubeVertexes.push(uvAry[0],uvAry[1]);
				flag[0] = point;
				point++;
				
				y = 1;
				_cubeVertexes.push(x,y,z);
				_cubeVertexes.push(uvAry[2],uvAry[3]);
				flag[1] = point;
				point++;
				
				angle+=30;
				x = radius*Math.cos(angle*Math.PI/180);
				z = radius*Math.sin(angle*Math.PI/180);
				y = 1;
				_cubeVertexes.push(x,y,z);
				_cubeVertexes.push(uvAry[4],uvAry[5]);
				flag[2] = point;
				point++; 
				
				y = -1;
				_cubeVertexes.push(x,y,z);
				_cubeVertexes.push(uvAry[6],uvAry[7]);
				flag[3] = point;
				point++;
				
				_cubeIndexes.push(flag[0],flag[1],flag[2],flag[2],flag[3],flag[0]);
			}
		}
		
	}
}

